#include "dataUP.h"

// 线程传递数据
pthread_data_t data;

volatile MQTTClient_deliveryToken deliveredtoken;

int main(int argc, char *argv[])
{

    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    data.pubmsg = pubmsg;
    data.conn_opts = conn_opts;
    int rc;
    int ch;
    // 解析点表
    data.nodeFile = fopen("../node.json", "r+");
    if (data.nodeFile == NULL)
    {
        printf("open node.json err\n");
        return -1;
    }
    // 获取文件内容
    cJSON *nodeRoot;
    char node[SIZE] = {0};
    int ret = fread(node, 1, SIZE, data.nodeFile);
    if (ret <= 0)
    {
        printf("fread err\n");
        return -1;
    }
    nodeRoot = cJSON_Parse(node);

    char address[32] = {0};
    sprintf(address, "tcp://%s:%d",
            cJSON_GetObjectItem(cJSON_GetArrayItem(cJSON_GetObjectItem(nodeRoot, "addr"), 3), "addr")->valuestring,
            cJSON_GetObjectItem(cJSON_GetArrayItem(cJSON_GetObjectItem(nodeRoot, "addr"), 3), "port")->valueint);
    // printf("address = %s\n", address);

    // 创建客户端
    MQTTClient_create(&data.client, address, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);
    data.conn_opts.keepAliveInterval = 20;
    data.conn_opts.cleansession = 1;

    // 设置回调函数
    MQTTClient_setCallbacks(data.client, NULL, connlost, msgarrvd, delivered);

    // 连接服务器
    if ((rc = MQTTClient_connect(data.client, &data.conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        printf("Failed to connect, return code %d\n", rc);
        exit(EXIT_FAILURE);
    }

    if (MQTTClient_subscribe(data.client, TOPICDOWN, QOS) == MQTTCLIENT_SUCCESS)
    {
        printf("subscribe success\n");
    }

    // 建立数据库
    // 病人库
    if (creat_patientDB(&data.patientDB) != 0)
    {
        printf("creat_patientDB err\n");
        return -1;
    }
    creat_patientIDTable(data.patientDB); // 创建病人ID表
    // 病房库
    if (creat_roomDB(&data.roomDB) != 0)
    {
        printf("creat_roomDB err\n");
        return -1;
    }
    // 用户库
    if (creat_userDB(&data.userDB) != 0)
    {
        printf("creat_userDB err\n");
        return -1;
    }

    // 获取模式
    data.mode = cJSON_GetObjectItem(nodeRoot, "mode")->valueint;
    // 获取周期
    data.period = cJSON_GetObjectItem(nodeRoot, "period")->valueint;

    int patientNum = cJSON_GetArraySize(cJSON_GetObjectItem(nodeRoot, "patient"));
    // printf("patientNum = %d\n", patientNum);
    int roomNum = cJSON_GetArraySize(cJSON_GetObjectItem(nodeRoot, "room"));
    // printf("roomNum = %d\n", roomNum);
    int userNum = cJSON_GetArraySize(cJSON_GetObjectItem(nodeRoot, "user"));
    // printf("userNum = %d\n", userNum);

    // 创建消息队列
    data.msgid = creat_msg();
    while (data.msgid <= 0)
    {
        data.msgid = creat_msg();
    }

    // 创建并将数据放到共享内存中
    int shmid;
    data.shmaddr = creat_shm(&shmid);
    std_node_t msgaddr[27] = {0};
    int i = 0;
    // 解析病人数据
    while (i < patientNum)
    {
        cJSON *item = cJSON_GetArrayItem(cJSON_GetObjectItem(nodeRoot, "patient"), i);
        cJSON *key = cJSON_GetObjectItem(item, "key");
        cJSON *name = cJSON_GetObjectItem(item, "name");
        cJSON *type = cJSON_GetObjectItem(item, "type");
        msgaddr[i].key = key->valueint;
        strcpy(msgaddr[i].name, name->valuestring);
        msgaddr[i].type = type->valueint;
        msgaddr[i].ret = -1;
        i++;
    }
    // 解析病房数据
    while (i < roomNum + patientNum)
    {
        cJSON *item = cJSON_GetArrayItem(cJSON_GetObjectItem(nodeRoot, "room"), i - patientNum);
        cJSON *key = cJSON_GetObjectItem(item, "key");
        cJSON *name = cJSON_GetObjectItem(item, "name");
        cJSON *type = cJSON_GetObjectItem(item, "type");
        msgaddr[i].key = key->valueint;
        strcpy(msgaddr[i].name, name->valuestring);
        msgaddr[i].type = type->valueint;
        msgaddr[i].ret = -1;
        i++;
    }
    // 解析用户数据
    while (i < userNum + roomNum + patientNum)
    {
        cJSON *item = cJSON_GetArrayItem(cJSON_GetObjectItem(nodeRoot, "user"), i - roomNum - patientNum);
        cJSON *key = cJSON_GetObjectItem(item, "key");
        cJSON *name = cJSON_GetObjectItem(item, "name");
        cJSON *type = cJSON_GetObjectItem(item, "type");
        msgaddr[i].key = key->valueint;
        strcpy(msgaddr[i].name, name->valuestring);
        msgaddr[i].type = type->valueint;
        msgaddr[i].ret = -1;
        i++;
    }
    memcpy(data.shmaddr, msgaddr, sizeof(msgaddr));

    // 创建上报数据子线程
    pthread_t tUpdataID;
    ret = pthread_create(&tUpdataID, NULL, thread_updata, NULL);

    // 创建数据存储子线程
    pthread_t tStoreID;
    ret = pthread_create(&tStoreID, NULL, thread_store, NULL);

    // 创建控制指令子线程
    pthread_t tCmdID;
    ret = pthread_create(&tCmdID, NULL, thread_cmd, NULL);

    // // 订阅消息子线程
    // pthread_t tSubID;
    // ret = pthread_create(&tSubID, NULL, thread_sub, (void *)&data);

    // 等待线程结束
    pthread_join(tUpdataID, NULL);
    pthread_join(tStoreID, NULL);
    pthread_join(tCmdID, NULL);
    // pthread_join(tSubID, NULL);

    MQTTClient_unsubscribe(&data.client, TOPICDOWN);
    MQTTClient_disconnect(&data.client, 10000);
    MQTTClient_destroy(&data.client);
    return 0;
}

// 数据上报子线程
void *thread_updata(void *arg)
{
    // 分离线程
    pthread_detach(pthread_self());
    printf("上报数据\n");
    // 判断数据上报模式
    while (1)
    {
        switch (data.mode)
        {
        case MODE_FLASH:
            // dataFlash(&data);
            break;
        case MODE_TIME:
            dataTime(&data);
            break;
        case MODE_CHANGE:
            // 先上报一次所有数据
            dataUpdata(&data, -1);
            dataChange(&data);
            break;
        default:
            break;
        }
    }
}

// 数据存储子线程
void *thread_store(void *arg)
{
    // 分离线程
    pthread_detach(pthread_self());
    printf("存储数据\n");
    char sql[128] = {0};
    char *buf = (char*)malloc(1024);
    // 创建数据表
    while (1)
    {
        // 创建病人表
        // 拿到当前的病人ID
        int patientID = 0;
        int i = 0;
        int flag = 0;
        for (i = 0; i < 11; i++)
        {
            // 如果数据没有更新
            if (data.shmaddr[i].new_val.i_val == data.shmaddr[i].old_val.i_val)
            {
                continue;
            }
            printf("病人信息更新\n");
            if (data.shmaddr[i].key == 101)
            {
                printf("patient old ID %d\n", data.shmaddr[i].old_val.i_val);
                printf("patient new ID %d\n", data.shmaddr[i].new_val.i_val);
                // 创建病人表
                if (creat_patientTable(data.patientDB, data.shmaddr[i].new_val.i_val) != 0)
                {
                    printf("create patient table failed\n");
                    continue;
                }
            }
            // printf("key %d\n", data.shmaddr[i].key);
            // printf("old  %.2f\n", data.shmaddr[i].old_val.f_val);
            // printf("new  %.2f\n", data.shmaddr[i].new_val.f_val);
            flag = 1;
            data.shmaddr[i].old_val = data.shmaddr[i].new_val;
        }
        if (flag == 1)
        {
            // 向病人表中添加数据
            if (insert_patient(data.patientDB, data.shmaddr) != 0)
            {
                printf("insert patient data failed\n");
            }
            flag = 0;
        }
        // 创建病房表
        int roomID = 0;
        for (; i < 22; i++)
        {
            // 如果数据有更新
            if (data.shmaddr[i].new_val.i_val == data.shmaddr[i].old_val.i_val)
            {
                continue;
            }
            printf("病房数据更新\n");
            if (data.shmaddr[i].key == 201)
            {
                // 创建病房表
                if (creat_roomTable(data.roomDB, data.shmaddr[i].new_val.i_val) != 0)
                {
                    printf("create room table failed\n");
                    continue;
                }
            }
            flag = 1;
            data.shmaddr[i].old_val = data.shmaddr[i].new_val;
        }
        if (flag == 1)
        {
            // 向病房表中添加数据
            if (insert_room(data.patientDB, data.shmaddr) != 0)
            {
                printf("insert room data failed\n");
                continue;
            }
        }
        // 创建用户表
        for (; i < 25; i++)
        {
            // 如果数据有更新
            if (data.shmaddr[i].new_val.i_val == data.shmaddr[i].old_val.i_val)
            {
                continue;
            }
            flag = 1;
            data.shmaddr[i].old_val = data.shmaddr[i].new_val;
        }
        if (flag == 1)
        {
            flag = 0;
            // 遍历用户表，如果用户表中不存在用户名那么向表中插入用户名，
            // 如果表中存在用户名且权限为0核对密码，如果权限值为1则更新密码
            memset(sql, 0, sizeof(sql));
            sprintf(sql, "select * from user where username = '%s'", data.shmaddr[22].new_val.str);
            int ret = select_value(data.userDB, "user", sql, &buf);
            printf("ret = %d\n", ret);
            if (ret == 0)
            { 
                // 向用户表中添加数据
                if (insert_user(data.userDB, data.shmaddr) != 0)
                {
                    printf("insert user data failed\n");
                    continue;
                }
            }
            else
            {
                // 更新用户表中的数据
                if(data.shmaddr[24].new_val.i_val == 1)
                {
                    printf("修改密码\n");
                    memset(sql, 0, sizeof(sql));
                    sprintf(sql, "update user set password = '%s' where username = '%s'", data.shmaddr[23].new_val.str, data.shmaddr[22].new_val.str);
                    if(update_value(data.userDB, "user", sql) != 0)
                    {
                        printf("update user data failed\n");
                        continue;
                    }
                }
                else
                {
                    // 核对密码是否正确
                    printf("buf = %s\n", buf);
                    printf("密码正确\n");
                    // if(strcmp(buf[5], data.shmaddr[23].new_val.str) == 0)
                    // {
                    //     printf("密码正确\n");
                    // }
                }
            }
            
        }
    }
}

// 控制指令子线程
void *thread_cmd(void *arg)
{
    // 分离线程
    pthread_detach(pthread_self());
    printf("控制指令\n");

    msg_t msg1 = {0};
    msg_t msg2 = {0};

    int j = 0;

    while (1)
    {
        sleep(1);
        // 控制设备指令
        for (int i = 0; i < 25; i++)
        {
            // 如果数据有更新
            if (data.shmaddr[i].new_val.i_val != data.shmaddr[i].old_val.i_val)
            {
                switch (data.shmaddr[i].key)
                {
                case 106:
                case 107:
                case 204:
                case 205:
                case 206:
                case 208:
                case 209:
                case 210:
                case 211:
                    if (send_msg(data.msgid, data.shmaddr[i].key, &data.shmaddr[i]) != 0)
                    {
                        printf("send msg failed\n");
                    }
                    else
                    {
                        printf("send msg success\n");
                    }
                    break;
                default:
                    break;
                }
                data.shmaddr[i].old_val = data.shmaddr[i].new_val;
            }
        }
        // 刷新上报更改指令
        if (data.mode == MODE_FLASH)
        {
            recv_msg(data.msgid, &msg1, 3, IPC_NOWAIT);
            if (msg1.data.new_val.i_val == 1)
            {
                dataFlash(&data);
            }
            msg1.data.new_val.i_val = 0;
        }
        // 修改mode
        if (recv_msg(data.msgid, &msg2, 2, IPC_NOWAIT) > 0 && msg2.data.new_val.i_val != data.mode)
        {
            printf("修改mode为：%d\n", msg2.data.new_val.i_val);
            rewind(data.nodeFile); // 刷新文件指针
            char s[32] = {0};
            // 将文件流指针移动到mode后面
            int j = 0;
            for (int i = 0; j != 2; i++)
            {
                if (fgetc(data.nodeFile) == ',')
                {
                    j++;
                }
            }
            fseek(data.nodeFile, -2, SEEK_CUR);
            // 写入新的mode
            fputc(msg2.data.new_val.i_val + '0', data.nodeFile);
            // 刷新缓冲
            fflush(data.nodeFile);
            msg2.data.new_val.i_val = msg2.data.old_val.i_val;
        }
    }
}

// 订阅消息子线程
void *thread_sub(void *arg)
{
    // 分离线程
    pthread_detach(pthread_self());
    printf("订阅消息\n");
    pthread_data_t *data = (pthread_data_t *)arg;
    char *topic = TOPICDOWN;
    int topicLen = strlen(topic);
    // 订阅消息
    MQTTClient_subscribe(data->client, TOPICDOWN, QOS);
    while (1)
        ;
}

// 刷新的形式进行数据上报
void dataFlash(pthread_data_t *data)
{

    // 刷新上报所有数据
    dataUpdata(data, -1);
}

// 定时的形式进行数据上报
void dataTime(pthread_data_t *data)
{
    // 阻塞定时
    dataUpdata(data, -1);
    printf("定时上报\n");
    sleep(data->period / 1000);
}

// 变化的形式进行数据上报
void dataChange(pthread_data_t *data)
{
    // 循环遍历共享内存中的数据
    while (1)
    {
        for (int i = 0; i < 25; i++)
        {
            // 如果有新数据
            if (data->shmaddr[i].new_val.i_val != data->shmaddr[i].old_val.i_val)
            {
                // 数据上报
                dataUpdata(data, i);
            }
        }
    }
}

// 数据上报
void dataUpdata(pthread_data_t *data, int number)
{
    // 将共享内存中的数据转化成ｊｓｏｎ结构
    cJSON *root = cJSON_CreateObject();
    cJSON *arr = cJSON_CreateArray();
    if (number == -1)
    {
        // 上报所有数据
        // 循环遍历共享内存中的数据
        for (int i = 0; i < 25; i++)
        {
            cJSON *item = cJSON_CreateObject();
            cJSON_AddItemToObject(item, "key", cJSON_CreateNumber(data->shmaddr[i].key));
            cJSON_AddItemToObject(item, "type", cJSON_CreateNumber(data->shmaddr[i].type));
            cJSON_AddItemToObject(item, "name", cJSON_CreateString(data->shmaddr[i].name));
            switch (data->shmaddr[i].type)
            {
            case 3:
                cJSON_AddItemToObject(item, "new_val", cJSON_CreateNumber(data->shmaddr[i].new_val.i_val));
                cJSON_AddItemToObject(item, "old_val", cJSON_CreateNumber(data->shmaddr[i].old_val.i_val));
                break;
            case 4:
                cJSON_AddItemToObject(item, "new_val", cJSON_CreateString(data->shmaddr[i].new_val.str));
                cJSON_AddItemToObject(item, "old_val", cJSON_CreateString(data->shmaddr[i].old_val.str));
                break;
            default:
                break;
            }
            cJSON_AddItemToObject(item, "ret", cJSON_CreateNumber(data->shmaddr[i].ret));
            cJSON_AddItemToArray(arr, item);
        }
    }
    else
    {
        // 上报指定数据
        int i = number;
        cJSON *item = cJSON_CreateObject();
        cJSON_AddItemToObject(item, "key", cJSON_CreateNumber(data->shmaddr[i].key));
        cJSON_AddItemToObject(item, "type", cJSON_CreateNumber(data->shmaddr[i].type));
        cJSON_AddItemToObject(item, "name", cJSON_CreateString(data->shmaddr[i].name));
        switch (data->shmaddr[i].type)
        {
        case 3:
            cJSON_AddItemToObject(item, "new_val", cJSON_CreateNumber(data->shmaddr[i].new_val.i_val));
            cJSON_AddItemToObject(item, "old_val", cJSON_CreateNumber(data->shmaddr[i].old_val.i_val));
            break;
        case 4:
            cJSON_AddItemToObject(item, "new_val", cJSON_CreateString(data->shmaddr[i].new_val.str));
            cJSON_AddItemToObject(item, "old_val", cJSON_CreateString(data->shmaddr[i].old_val.str));
            break;
        default:
            break;
        }
        cJSON_AddItemToObject(item, "ret", cJSON_CreateNumber(data->shmaddr[i].ret));
        cJSON_AddItemToArray(arr, item);
    }

    cJSON_AddItemToObject(root, "data", arr);
    char json[SIZE] = {0};
    strcpy(json, cJSON_Print(root));
    // printf("%s\n", json);

    // 设置发布消息
    data->pubmsg.payload = json;
    data->pubmsg.payloadlen = sizeof(json);
    data->pubmsg.qos = QOS;
    data->pubmsg.retained = 0;
    MQTTClient_publishMessage(data->client, TOPICUP, &data->pubmsg, &data->token);
    MQTTClient_waitForCompletion(data->client, data->token, TIMEOUT);
    cJSON_Delete(root);
    memset(json, 0, SIZE);
}

// 发布消息确认回调函数
void delivered(void *context, MQTTClient_deliveryToken dt)
{
    printf("Message with token value %d delivery confirmed\n", dt);
    deliveredtoken = dt;
}

// 消息接收回调函数
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
    // 打开消息队列
    data.msgid = creat_msg();
    while (data.msgid <= 0)
    {
        data.msgid = creat_msg();
    }
    msg_t msg = {0};
    // 解析json
    cJSON *root = cJSON_Parse((char *)message->payload);
    cJSON *mode = cJSON_GetObjectItem(root, "mode");
    cJSON *flash = cJSON_GetObjectItem(root, "flash");
    cJSON *patient = cJSON_GetObjectItem(root, "patient");
    cJSON *room = cJSON_GetObjectItem(root, "room");
    cJSON *user = cJSON_GetObjectItem(root, "user");
    if (patient != NULL) // QT界面从内存中获取病人数据
    {
        cJSON *arr = cJSON_CreateArray();
        for (int i = 0; i < 11; i++)
        {
            cJSON *item = cJSON_CreateObject();
            cJSON_AddItemToObject(item, "key", cJSON_CreateNumber(data.shmaddr[i].key));
            cJSON_AddItemToObject(item, "type", cJSON_CreateNumber(data.shmaddr[i].type));
            cJSON_AddItemToObject(item, "name", cJSON_CreateString(data.shmaddr[i].name));
            if (data.shmaddr[i].type == 3)
            {
                switch (data.shmaddr[i].key)
                {
                case 106:
                case 107:
                    cJSON_AddItemToObject(item, "value", cJSON_CreateNumber(data.shmaddr[i].new_val.f_val));
                    break;
                default:
                    cJSON_AddItemToObject(item, "value", cJSON_CreateNumber(data.shmaddr[i].new_val.i_val));
                    break;
                }
            }
            else if (data.shmaddr[i].type = 4)
            {
                cJSON_AddItemToObject(item, "value", cJSON_CreateString(data.shmaddr[i].new_val.str));
            }
            cJSON_AddItemToArray(arr, item);
        }
        cJSON *msg = cJSON_CreateObject();
        cJSON_AddItemToObject(msg, "data", arr);
        char json[SIZE] = {0};
        strcpy(json, cJSON_Print(msg));
        // printf("json: %s\n", json);
        data.pubmsg.payload = json;
        data.pubmsg.payloadlen = sizeof(json);
        data.pubmsg.qos = QOS;
        data.pubmsg.retained = 0;
        MQTTClient_publishMessage(data.client, TOPICUP, &data.pubmsg, &data.token);
    }
    if (room != NULL)
    {
        cJSON *arr = cJSON_CreateArray();
        for (int i = 11; i < 22; i++)
        {
            cJSON *item = cJSON_CreateObject();
            cJSON_AddItemToObject(item, "key", cJSON_CreateNumber(data.shmaddr[i].key));
            cJSON_AddItemToObject(item, "type", cJSON_CreateNumber(data.shmaddr[i].type));
            cJSON_AddItemToObject(item, "name", cJSON_CreateString(data.shmaddr[i].name));
            if (data.shmaddr[i].key == 207)
            {
                cJSON_AddItemToObject(item, "value", cJSON_CreateString(data.shmaddr[i].new_val.str));
            }
            cJSON_AddItemToObject(item, "value", cJSON_CreateNumber(data.shmaddr[i].new_val.i_val));
            cJSON_AddItemToArray(arr, item);
        }
        cJSON *msg = cJSON_CreateObject();
        cJSON_AddItemToObject(msg, "data", arr);
        char json[SIZE] = {0};
        strcpy(json, cJSON_Print(msg));
        // printf("json: %s\n", json);
        data.pubmsg.payload = json;
        data.pubmsg.payloadlen = sizeof(json);
        data.pubmsg.qos = QOS;
        data.pubmsg.retained = 0;
        MQTTClient_publishMessage(data.client, TOPICUP, &data.pubmsg, &data.token);
    }
    if (user != NULL)
    {
        cJSON *arr = cJSON_CreateArray();
        for (int i = 22; i < 25; i++)
        {
            cJSON *item = cJSON_CreateObject();
            cJSON_AddItemToObject(item, "key", cJSON_CreateNumber(data.shmaddr[i].key));
            cJSON_AddItemToObject(item, "type", cJSON_CreateNumber(data.shmaddr[i].type));
            cJSON_AddItemToObject(item, "name", cJSON_CreateString(data.shmaddr[i].name));
            if (data.shmaddr[i].key == 303)
            {
                cJSON_AddItemToObject(item, "value", cJSON_CreateNumber(data.shmaddr[i].new_val.i_val));
            }
            cJSON_AddItemToObject(item, "value", cJSON_CreateString(data.shmaddr[i].new_val.str));
            cJSON_AddItemToArray(arr, item);
        }
        cJSON *msg = cJSON_CreateObject();
        cJSON_AddItemToObject(msg, "data", arr);
        char json[SIZE] = {0};
        strcpy(json, cJSON_Print(msg));
        // printf("json: %s\n", json);
        data.pubmsg.payload = json;
        data.pubmsg.payloadlen = sizeof(json);
        data.pubmsg.qos = QOS;
        data.pubmsg.retained = 0;
        MQTTClient_publishMessage(data.client, TOPICUP, &data.pubmsg, &data.token);
    }
    if (mode != NULL)
    {
        msg.data.key = 2;
        msg.data.new_val.i_val = mode->valueint;
        send_msg(data.msgid, 2, &msg.data);
    }
    if (flash != NULL)
    {
        msg.data.key = 1;
        msg.data.new_val.i_val = flash->valueint;
        int ret = send_msg(data.msgid, 3, &msg.data);
    }

    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);
    return 1;
}

// 连接丢失回调函数
void connlost(void *context, char *cause)
{
    printf("\nConnection lost\n");
    printf("     cause: %s\n", cause);
}
